package com.chinamsp.dydb.configuration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.CollectionUtils;

import com.chinamsp.dydb.DydbContstants;
import com.chinamsp.dydb.convert.ORMNameConverter;
import com.chinamsp.dydb.dbsource.DataSourceImpl;
import com.chinamsp.dydb.dbsource.DataSourceManager;
import com.chinamsp.dydb.dbsource.DataSourceSettings;
import com.chinamsp.dydb.dbsource.DynamicDataSource;
import com.chinamsp.dydb.dbsource.DynamicDataSourceContextHolder;

/**
 * 动态数据源自动扫描配置
 * 
 * @author shifenghu
 *
 */
@Configuration
@Import(ORMNameConverter.class)
@ImportAutoConfiguration({MybatisInterceptorConfigration.class, MyBatisSqlSessionFactoryConfigration.class})
@EnableConfigurationProperties({DataSourceSettings.class})
@ConditionalOnProperty(prefix = DydbContstants.PROPERTIES_PREFIX, name = "url")
public class DynamicDataSourceAutoConfiguration {

	private ConversionService conversionService = new DefaultConversionService();

	/**
	 * 数据源管理
	 */
	private DataSourceManager dataSourceManager;

	public DynamicDataSourceAutoConfiguration() {
		dataSourceManager = new DataSourceManager();
	}

	@Bean
	@Primary
	@Order(Ordered.LOWEST_PRECEDENCE)
	public DataSource dynamicDataSource(DataSourceSettings settings) {
		// 从 spring propeties 中获取 settings testbean
		// DataSourceSettings settings = resolveSettings();
		// 转换并初始化
		settings.initialize(dataSourceManager);
		// 开始 初始化数据源
		// 初始化 主库
		DataSourceImpl impl;
		// 主数据源
		for (String key : dataSourceManager.getMasters().keySet()) {
			impl = dataSourceManager.getMasters().get(key);
			// 如果是主数据源
			if (key.equals(settings.getDefaultSource())) {
				dataSourceManager.setDefaultSource(impl);
				dataSourceManager.setDefaultSourceName(key);
			}
			key += DataSourceManager.MASTER_SUFFIX;
			createDsBean(key, settings.getDataSourceTypeName(), impl);

		}
		// 初始化从库
		List<DataSourceImpl> datasources;
		for (String key : dataSourceManager.getSlaves().keySet()) {
			datasources = dataSourceManager.getSlaves().get(key);
			if (!CollectionUtils.isEmpty(datasources)) {
				for (int i = 0; i < datasources.size(); i++) {
					impl = datasources.get(i);
					key += DataSourceManager.SLAVE_SUFFIX + '-' + i;
					createDsBean(key, settings.getDataSourceTypeName(), impl);
				}
			}
		}
		// 设置到 上下文中
		DynamicDataSourceContextHolder.setDataSourceManager(dataSourceManager);
		// 初始化 一个 动态数据源
		DynamicDataSource dataSource = new DynamicDataSource();
		dataSource.setDataSourceManager(dataSourceManager);
		// 这里面 new 一个 空的 DataSource target
		dataSource.setTargetDataSources(new HashMap<Object, Object>(2));
		dataSource.setDefaultTargetDataSource(dataSourceManager.getDefaultSource().getDatasource());
		return dataSource;
	}

	/**
	 * 创建bean
	 * 
	 * @param beanName
	 * @param datasourceTypeName
	 *            数据源类型名称
	 * @param impl
	 *            数据源参数实例
	 */
	private DataSource createDsBean(String beanName, String datasourceTypeName, DataSourceImpl impl) {
		DataSource dataSource = buidDataSource(beanName, datasourceTypeName, impl);
		binderExtendConfig(beanName, impl, dataSource);
		impl.setDatasource(dataSource);
		return dataSource;
	}

	@SuppressWarnings("unchecked")
	private DataSource buidDataSource(String beanName, String dataSourceTypeName, DataSourceImpl impl) {
		Class<? extends DataSource> dataSourceTypeClass;
		try {
			dataSourceTypeClass = (Class<? extends DataSource>) Class.forName(dataSourceTypeName);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(
					"dynamic data source config error, ClassNotFoundException:  " + impl.getDriverClassName(), e);
		}
		DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(impl.getDriverClassName())
				.url(impl.getUrl()).username(impl.getUsername()).password(impl.getPassword()).type(dataSourceTypeClass);
		return factory.build();
	}

	/**
	 * 合并属性
	 * 
	 * @param impl
	 * @param beanName
	 *            设置bean name
	 * @return
	 */
	private Map<String, Object> mergeProperties(DataSourceImpl impl, String beanName) {
		Map<String, Object> result = new HashMap<String, Object>(16);
		// 初始化扩展
		if (!CollectionUtils.isEmpty(impl.getProperties())) {
			result.putAll(impl.getProperties());
		}
		impl.setBeanName(beanName);
		;
		return result;
	}

	/**
	 * 设置数据源 其他扩展参数
	 * 
	 * @param dataSourceName
	 * @param impl
	 * @param dataSource
	 */
	private void binderExtendConfig(String dataSourceName, DataSourceImpl impl, DataSource dataSource) {
		Map<String, Object> properties = mergeProperties(impl, dataSourceName);
		// 检查 扩展参数是否存在，如果不存在则忽略下面的绑定步骤
		if (properties.isEmpty()) {
			return;
		}
		RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
		dataBinder.setConversionService(conversionService);
		dataBinder.setIgnoreNestedProperties(false);// false
		dataBinder.setIgnoreInvalidFields(false);// false
		dataBinder.setIgnoreUnknownFields(true);// true
		dataBinder.bind(new MutablePropertyValues(properties));
	}

}
